package main

import (
	"fmt"
	uuid "github.com/satori/go.uuid"
	"io/ioutil"
	"math"
	"os"
	"regexp"
	"strings"
	"time"
)

const (
	DIRTYPE = "dir"
	CASETYPE = "testcase"
	TSVSUFFIX = ".tsv"
	TESTCASE = "*Test Cases*"
	TESTCASESTARTTAG = "*Test Cases*"
	TESTKEYWORDSTARTTAG = "*Keywords*"
	VARIABLESSTARTTAG = "*Variables*"
	SETTINGSTARTTAG = "*Settings*"
	TESTCASESEPARATOR = "\t\t\t\t\t\t\t"
	TAGS = "[Tags]"
)

type Node struct {
	testCenterCaseId string
	id               string
	nodeId           string
	pNodeId          string
	name             string
	tag              []string
	path             string
	nType            string
}

type Parse struct {
	Res []Node
}

func (p *Parse) Start(path string) {
	if ok, err := HasDir(path); !ok {
		if err != nil {
			panic(err)
		}
	}
	p.InProcess(strings.Split(path, string(os.PathSeparator)), []string{}, GetUUID())
}

func (p *Parse)InProcess(path []string, ids []string, myId string) {
	if !IsDir(strings.Join(path, string(os.PathSeparator))) {
		// 处理tsv文件逻辑
		p.parseFile(path, ids, myId)
		return
	}
	// 处理dir
	p.Res = append(p.Res, Node{
		pNodeId: p.GetParentId(ids),
		nodeId: myId,
		name: path[len(path)-1],
		nType: DIRTYPE,
		path: p.FormatPath(ids, myId),
	})

	// 递归处理dir
	files := GetFiles(strings.Join(path, string(os.PathSeparator)))
	for _, v := range files {
		path = append(path, v)
		ids  = append(ids, myId)
		p.InProcess(path, ids, GetUUID())
		path = path[:len(path)-1]
		ids  = ids[:len(ids)-1]
	}
}

func (p *Parse) parseFile(path []string, ids []string, myId string) {
	if len(path) > 0 && strings.HasSuffix(path[len(path)-1], TSVSUFFIX) {
		// tsv 处理
		p.Res = append(p.Res, Node{
			pNodeId: p.GetParentId(ids),
			nodeId: myId,
			name: strings.Split(path[len(path)-1], ".")[0],
			nType: DIRTYPE,
			path: p.FormatPath(ids, myId),
		})
		cases := p.GetTestCases(strings.Join(path, string(os.PathSeparator)))
		p.ParseCases(cases, ids, myId)
	}
}

func (p *Parse) ParseCases(cases []string, ids  []string, pId string){
	for _, oneCase := range cases {
		p.ParseCase(oneCase, ids, pId)
	}
}

func (p *Parse) ParseCase(oCase string, ids  []string, pId string){
	// 处理case
	name := p.GetTestCaseName(oCase)
	if name != "" {
		myId := GetUUID()
		p.Res = append(p.Res, Node{
			pNodeId: p.GetParentId(ids),
			nodeId: myId,
			name: name,
			tag: p.GetTestCaseTags(oCase),
			nType: CASETYPE,
			path: p.FormatPath(ids, pId) + string(os.PathSeparator) + myId,
		})
	}
}

func (p *Parse) GetTestCaseName(oCase string) string {
	return strings.Trim(strings.Split(oCase, "\t")[0], "\r\n")
}

func (p *Parse) GetTestCaseTags(oCase string) []string {
	if !strings.Contains(oCase, TAGS) {
		return []string{}
	}
	reg := regexp.MustCompile("(\\[Tags\\][\\W\\w]+?)\n")
	data := reg.Find([]byte(oCase))
	deletN := strings.Split(string(data), "\n")
	return strings.Split(deletN[0], "\t")[1:]
}

func  (p *Parse) GetParentId(ids []string) string {
	if len(ids) > 0 {
		return ids[len(ids)-1]
	}
	return ""
}

func (p *Parse) GetTestCases (path string) []string {
	content, err := os.ReadFile(path)
	if err != nil {
		panic(err)
	}
	contentStr := string(content)
	if strings.Contains(contentStr, TESTCASE) {
		start, end := p.GetTestCasesStartAndEndIndex(contentStr)
		var casesStr string
		if end > start && end != math.MaxInt64 {
			casesStr = string(content[start:end])
		} else {
			casesStr = string(content[start:])
		}
		caseArr := strings.Split(casesStr, TESTCASESEPARATOR)
		return caseArr[1:] // 去掉开头的*Test Case*，和结尾的空字符串
	}
	return []string{}
}

func (p *Parse) GetTestCasesStartAndEndIndex (tsvContent string) (s, e int) {
	s = strings.Index(tsvContent, TESTCASESTARTTAG)
	k := strings.Index(tsvContent, TESTKEYWORDSTARTTAG)
	v := strings.Index(tsvContent, VARIABLESSTARTTAG)
	t := strings.Index(tsvContent, SETTINGSTARTTAG)
	return s, RangeMin(s, k, v, t)
}

func (p *Parse) FormatPath (ids []string, myId string) string {
	if len(ids) == 0 {
		return string(os.PathSeparator) + myId
	}
	return string(os.PathSeparator)+strings.Join(ids, string(os.PathSeparator))+string(os.PathSeparator)+myId
}

func (p *Parse) FormatPrint(){
	for _, n := range p.Res {
		FormatPrint1(len(strings.Split(n.path, string(os.PathSeparator)))-2, n.name, 40)
	}
}

func RangeMin(min int, arr ...int) int {
	ans := math.MaxInt64
	for _, v := range arr {
		if v > min {
			ans = Min(ans, v)
		}
	}
	return ans
}

func Min(a, b int) int {
	if a > b {
		return b
	}
	return a
}

func GetFiles(path string) []string {
	ans := make([]string, 0)
	files, _ := ioutil.ReadDir(path)
	for _, f := range files {
		ans = append(ans, f.Name())
	}
	return ans
}

func GetNodeType(path string) string {
	if IsDir(path) {
		return DIRTYPE
	}
	return CASETYPE
}

func (p *Parse) GetRootName(path string) string {
	x := strings.Split(path, string(os.PathSeparator))
	return x[len(x)-1]
}

func GetUUID() string {
	u2 := uuid.NewV4()
	return u2.String()
}

// 判断所给路径是否为文件夹
func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		panic(err)
	}
	return s.IsDir()
}

func HasDir(path string) (bool, error) {
	_, _err := os.Stat(path)
	if _err == nil {
		return true, nil
	}
	if os.IsNotExist(_err) {
		return false, nil
	}
	return false, _err
}

func FormatPrint1(level int, name string, formatLen int){
	rightLen := formatLen-len(name)
	fmt.Println(GetSpace(level*8) + name + GetSpace(rightLen))
}

func GetSpace(num int) string {
	ans := ""
	for i:=0; i<num; i++{
		ans += " "
	}
	return ans
}

func main() {
	parse := Parse{} //Merge_CI2
	t := time.Now()
	//parse.Start("/home/10260444@zte.intra/rf/amc")
	//parse.Start("/home/10260444@zte.intra/rf/script_v3")
	//parse.Start("/home/10260444@zte.intra/rf/demo/Combine/Learn/Mo.tsv")
	parse.Start("/home/10260444@zte.intra/rf/demo")
	fmt.Println(time.Since(t))
	parse.FormatPrint()
}
